﻿
Imports System.Threading
Imports std
Imports storoomc

#Const TCPTALKTEST = False

Public Module storsync

    Public Class argument
        Public Const inputhost As String = "ih"
        Public Const inputport As String = "ip"
        Public Const outputhost As String = "oh"
        Public Const outputport As String = "op"
        Public Const roundsleep As String = "slp"
        Public Const threadcount As String = "tc"
        Public Const keyselection As String = "ks"

        Public Class defaultValue
#If DEBUG Then
            Public Const roundsleep As Int64 = 1000    '1 second
            Public Const threadcount As Int32 = 4
#Else
            Public Const roundsleep As Int64 = 5 * 60 * 1000    '5 minutes
            Public Const threadcount As Int32 = 16
#End If
        End Class
    End Class

    Private Sub writeHelp()
        writeConsoleline("storsync -" + argument.inputhost + " <source host name or ip> -" _
                         + argument.inputport + " <source port> -" _
                         + argument.outputhost + " <destination host name or ip> -" _
                         + argument.outputport + " <destination port> -" _
                         + argument.roundsleep + " <sleep ms between each round> -" _
                         + argument.threadcount + " <threads count> -" _
                         + argument.keyselection + " <key selection>")
    End Sub

    Private inputhost As String = Nothing
    Private inputport As Int32 = Nothing
    Private outputhost As String = Nothing
    Private outputport As Int32 = Nothing
    Private roundsleep As Int64 = 0
    Private threadcount As Int32 = 0
    Private keys As [set](Of String) = Nothing
    Private keysIt As [set](Of String).iterator = Nothing
    Private listtalk As tcpTalk = Nothing
    Private keyselection As String = Nothing

    Private Class syncpara
        Public italk As tcpTalk = Nothing
        Public otalk As tcpTalk = Nothing
    End Class

    Private threads() As Thread = Nothing
    Private paras() As syncpara = Nothing

    Private Sub syncthread(ByVal obj As Object)
        assert(Not keys Is Nothing, "keys is nothing.")
        assert(Not keysIt Is Nothing, "keysIt is nothing.")
        Dim para As syncpara = Nothing
        para = convertor.cast(Of syncpara)(obj, True)
        Dim buff As bytes = Nothing
        While keysIt <> keys.end() AndAlso Not consoleExtSignal()
            Dim key As String = Nothing
            SyncLock keysIt
                If keysIt <> keys.end() Then
                    key = +keysIt
                    keysIt += 1
                End If
            End SyncLock

            If fitfilter(keyselection, key) Then
#If TCPTALKTEST Then
                Dim italk As tcpTalk = Nothing
                Dim otalk As tcpTalk = Nothing
                If storclient.tryread(italk, inputhost, inputport, key, buff) <> returnValue.success Then
                    raiseError("cannot read from source host " + displayHostPort(inputhost, inputport) + _
                               " key " + key, errorHandle.errorType.warning)
                    Continue While
                End If

                If storclient.trymodify(otalk, outputhost, outputport, key, buff) <> returnValue.success Then
                    raiseError("cannot write to destination host " + displayHostPort(outputhost, outputport) + _
                               " key " + key, errorHandle.errorType.warning)
                End If
#Else
                If storclient.tryread(para.italk, inputhost, inputport, key, buff) <> returnValue.success Then
                    raiseError("cannot read from source host " + displayHostPort(inputhost, inputport) + _
                               " key " + key, errorHandle.errorType.warning)
                    Continue While
                End If

                If storclient.trymodify(para.otalk, outputhost, outputport, key, buff) <> returnValue.success Then
                    raiseError("cannot write to destination host " + displayHostPort(outputhost, outputport) + _
                               " key " + key, errorHandle.errorType.warning)
                End If
#End If
            End If
        End While
    End Sub

    Private Sub sync()
        If storclient.trylist(listtalk, inputhost, inputport, keys) <> returnValue.success Then
            raiseError("cannot list from source host " + displayHostPort(inputhost, inputport) _
                       + ", wait for next round.", errorHandle.errorType.warning)
            Return
        End If

        raiseError("got " + Convert.ToString(keys.size()) + " keys to sync.", errorHandle.errorType.application)
        keysIt = keys.begin()

        If paras Is Nothing Then
            ReDim paras(threadcount - 1)
        End If
        If threads Is Nothing Then
            ReDim threads(threadcount - 1)
        End If

        Dim i As Int32
        For i = 0 To threadcount - 1
            If paras(i) Is Nothing Then
                paras(i) = New syncpara()
            End If
            threads(i) = startThread(AddressOf syncthread, paras(i))
        Next

        For i = 0 To threadcount - 1
            If Not threads(i) Is Nothing AndAlso threads(i).IsAlive Then
                threads(i).Join()
            End If
        Next
    End Sub

    Private Function ishelpArgument(ByVal args() As String)
        Return args.Length() = 0 OrElse _
                strsame(args(0), "h", False) OrElse _
                strsame(args(0), "help", False) OrElse _
                strsame(args(0), "-h", False) OrElse _
                strsame(args(0), "-help", False)
    End Function

    Public Sub main(ByVal iargs() As String)
        debugpause()

        If ishelpArgument(iargs) Then
            writeHelp()
            Return
        End If

        startmain()
        errorHandle.skipErrorType += errorHandle.errorTypeChar(errorHandle.errorType.application)

        Dim args As map(Of String, vector(Of String)) = Nothing
        argmgr.parse(iargs, args, True)
        inputhost = argmgr.firstvalue(args, argument.inputhost, True)
        assert(Not isemptystring(inputhost), "do not get valid " + argument.inputhost + " argument.")
        assert(convertor.toint32(argmgr.firstvalue(args, argument.inputport, True), inputport) AndAlso inputport <> 0 _
               , "do not get valid " + argument.inputport + " argument.")
        outputhost = argmgr.firstvalue(args, argument.outputhost, True)
        assert(Not isemptystring(outputhost), "do not get valid " + argument.outputhost + " argument.")
        assert(convertor.toint32(argmgr.firstvalue(args, argument.outputport, True), outputport) AndAlso outputport <> 0 _
               , "do not get valid " + argument.outputport + " argument.")
        Dim str As String
        str = argmgr.firstvalue(args, argument.roundsleep, True)
        If isemptystring(str) Then
            roundsleep = argument.defaultValue.roundsleep
        Else
            assert(convertor.toint64(str, roundsleep), "do not get valid " + argument.roundsleep + " argument.")
        End If
        str = argmgr.firstvalue(args, argument.threadcount, True)
        If isemptystring(str) Then
            threadcount = argument.defaultValue.threadcount
        Else
            assert(convertor.toint32(str, threadcount), "do not get valid " + argument.threadcount + " argument.")
        End If
        keyselection = argmgr.firstvalue(args, argument.keyselection, True)
        If isemptystring(keyselection) Then
            keyselection = stringfilter.multiCharSign
        End If

        raiseError("sync from " + displayHostPort(inputhost, inputport) _
                   + " to " + displayHostPort(outputhost, outputport) _
                   + ", sleep between rounds for " + Convert.ToString(roundsleep) + " ms" _
                   + ", threadcount " + Convert.ToString(threadcount))
        Dim lastms As Int64 = 0
        While Not consoleExtSignal()
            If ticksToMilliseconds(Now.Ticks()) - lastms < roundsleep Then
                sleep()
            Else
                raiseError("start sync")
                sync()
                lastms = ticksToMilliseconds(Now.Ticks())
                raiseError("finish sync")
            End If
        End While

        If Not paras Is Nothing AndAlso paras.Length() > 0 Then
            Dim i As Int32
            For i = 0 To threadcount - 1
                If i < paras.Length() AndAlso Not paras(i) Is Nothing Then
                    If Not paras(i).italk Is Nothing Then
                        paras(i).italk.reset()
                        paras(i).italk = Nothing
                    End If
                    If Not paras(i).otalk Is Nothing Then
                        paras(i).otalk.reset()
                        paras(i).otalk = Nothing
                    End If
                End If
            Next
            If Not listtalk Is Nothing Then
                listtalk.reset()
                listtalk = Nothing
            End If
        End If

        finishmain()
    End Sub

End Module
